<?php
declare(strict_types=1);

namespace WebApp\lib\db\mysql;

use App\Lib\SwoftUtil;
use ErrorException;
use PDOException;
use txz\common\third_lib\model\util\DbUtil;
use WebApp\lib\exception\ClientOkException;
use WebApp\lib\util\Util;

/**
 * Class NormalTable
 * @package WebApp\lib\db
 */
class NormalTableV2 extends MysqlTable implements ITable
{
    private $debug = false;

    public function __construct()
    {
        if (Util::hostIsTest()) $this->debug = false;
    }

    /**
     * @param $arr
     * @return mixed
     */
    public function transaction($arr)
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$arr) {
            $dao_instance = $exe->getDaoInstance($db_name);
//            DbUtil::getInstance()->beginTransaction($dao_instance);
            try {
                foreach ($arr as $res) {
                    if (gettype($res) !== 'object') {
                        throw new ErrorException();
                    }
                    $res = (bool)$res();
                    if (!$res) {
                        throw new ErrorException();
                    }
                }
//                DbUtil::getInstance()->commitTransaction($dao_instance);
                return true;
            } catch (PDOException $e) {
//                DbUtil::getInstance()->rollBackTransaction($dao_instance);
                throw $e;
            } catch (ErrorException $e) {
//                DbUtil::getInstance()->rollBackTransaction($dao_instance);
                throw $e;
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    /**
     * @param string $sql
     * @param array $data
     * @return mixed
     */
    public function getObjArrBySql(string $sql, $data=[])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)" . $sql);
        $res = function (IMysqlExecute $exe) use ($sql, $data) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $this->setLastSql($sql);
            $res = DbUtil::getInstance()->executeSql($sql, $data, $dao_instance);
            return $res;
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getObjBySql(string $sql, $data=[])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use ($sql, $data) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $this->setLastSql($sql);
            $res = DbUtil::getInstance()->executeSql($sql, $data, $dao_instance);
            if (!is_array($res) || !$res) {
                return [];
            } else {
                return $res[0] ?? [];
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function updateObj($db_name, $data = [], $where = [])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$data, &$where) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $str = $this->makeWhere($where);
            $arr = MysqlTable::makeSetData($data);
            $set = $arr[0];
            $data = $arr[1];
            $sql = "UPDATE $db_name $set $str";
            $params = array_merge($data, $where);
            $this->setLastSql($sql);
            $this->setLastParams($params);
            return DbUtil::getInstance()->executeSql($sql, $params, $dao_instance);
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function removeObj($db_name, $where = [])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$where) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $str = $this->makeWhere($where);
            $sql = "DELETE FROM $db_name $str";
            $this->setLastSql($sql);
            $this->setLastParams($where);
            return DbUtil::getInstance()->executeSql($sql, $where, $dao_instance);
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function replaceObj($db_name, $data = [])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$data) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $arr = MysqlTable::makeSetData($data);
            $set = $arr[0];
            $data = $arr[1];
            $sql = "REPLACE INTO $db_name $set";
            $this->setLastSql($sql);
            $this->setLastParams($data);
            return DbUtil::getInstance()->executeSql($sql, $data, $dao_instance);
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function addObj($db_name, $data = [])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$data) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $arr = MysqlTable::makeSetData($data);
            $str = $arr[0];
            $data = $arr[1];
            $sql = "INSERT INTO $db_name $str";
//            throw new ClientOkException([
//                $sql, $data
//            ]);
            $this->setLastSql($sql);
            $this->setLastParams($data);
            $res = DbUtil::getInstance()->insertWithSql($sql, $data, $dao_instance);
            if ($res && is_numeric($res)) {
                return (int)$res;
            } else {
                Util::logJsonType(debug_backtrace(), '', 'w');
                Util::logJsonType([
                    $sql,
                    $data
                ], DbUtil::getInstance()->errMsg(), 'w');
                return false;
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getObjCount($db_name, $where = [])
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$where) {
            $str = $this->makeWhere($where);
            $_sql = "SELECT count(*) FROM $db_name $str";
            $this->setLastSql($_sql);

            $res = DbUtil::getInstance()->executeSql($_sql, $where, $exe->getDaoInstance($db_name));
            if (!is_array($res) || !$res) {
                return 0;
            } else {
                return intval(array_values($res[0])[0]);
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getObjArr($db_name, $need = [], $where = [], $group = '', $sort = '', $limit = '')
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$need, &$where, &$group, &$sort, &$limit) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $sql = "SELECT {$this->makeNeed($need)} FROM $db_name {$this->makeWhere($where)} {$this->makeGroup($group)} {$this->makeOrder($sort)} {$this->makeLimit($limit)}";
            $this->setLastSql($sql);
            $this->setLastParams($where);
            $res = DbUtil::getInstance()->insertWithSql($sql, $where, $dao_instance);

            if (!is_array($res) || !$res) {
                return [];
            } else {
                return $res;
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getObjArrV1(&$rows, string $db_name, $select, array $where = [], string $group = '', $sort = '', array $limit = []): callable
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$rows, &$db_name, &$select, &$where, &$group, &$sort, &$limit): void {
            $dao_instance = $exe->getDaoInstance($db_name);
            $sql = "SELECT {$this->makeNeedV1($select)} FROM $db_name {$this->makeWhere($where)} {$this->makeGroup($group)} {$this->makeOrder($sort)} {$this->makeLimitV1($limit)}";
            $this->setLastSql($sql);
            $this->setLastParams($where);
            $res = DbUtil::getInstance()->executeSql($sql, $where, $dao_instance);

            if (!is_array($res) || !$res) {
                $rows = [];
            } else {
                $rows = $res;
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getObj($db_name, $need = [], $where = [], $group = '', $sort = '')
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$db_name, &$need, &$where, &$group, &$sort) {
            $dao_instance = $exe->getDaoInstance($db_name);
            $_sql = "SELECT {$this->makeNeed($need)} FROM $db_name {$this->makeWhere($where)} {$this->makeGroup($group)} {$this->makeOrder($sort)} limit 1";
            $this->setLastSql($_sql);
            $this->setLastParams([
                "where" => $where,
                "need" => $need,
                "group" => $group,
                "sort" => $sort,
            ]);
            $res = DbUtil::getInstance()->executeSql($_sql, $where, $dao_instance);
            if (!is_array($res) || !$res) {
                return [];
            } else {
                return $res[0] ?? [];
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getObjV1(&$row, $db_name, $select, array $where = [], $group = '', $sort = ''): callable
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use (&$row, &$db_name, &$select, &$where, &$group, &$sort): void {
            $dao_instance = $exe->getDaoInstance($db_name);
            $_sql = "SELECT {$this->makeNeedV1($select)} FROM $db_name {$this->makeWhere($where)} {$this->makeGroup($group)} {$this->makeOrder($sort)} limit 1";
            $this->setLastSql($_sql);
            $this->setLastParams([
                "where" => $where,
                "need" => $select,
                "group" => $group,
                "sort" => $sort,
            ]);

            $res = DbUtil::getInstance()->executeSql($_sql, $where, $dao_instance);
            if (!is_array($res) || !$res) {
                $row = [];
            } else {
                $row = $res[0] ?? [];
            }
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    /**
     * @param string $_sql
     * @return mixed
     */
    public function execute(string $_sql)
    {
        if ($this->debug) $uni = uniqid();
        if ($this->debug) Util::logD(__FUNCTION__ . " start($uni)");
        $res = function (IMysqlExecute $exe) use ($_sql) {
            $this->setLastSql($_sql);
            $dao_instance = $exe->getDaoInstance($db_name);
            $res = DbUtil::getInstance()->executeSql($_sql, [], $dao_instance);
            return $res;
        };
        if ($this->debug) Util::logD(__FUNCTION__ . " end($uni)");
        return $res;
    }

    public function getLastSql()
    {
        return $this->last_sql;
    }

    public function getLastId()
    {
        return $this->last_id;
    }

    public function getLastErrInfo(): string
    {
        return $this->last_err_info;
    }

    public function getLastErrCode(): string
    {
        return $this->last_err_code;
    }

    public function setLastSql($sql)
    {
        $this->last_sql = $sql;
    }

    public function setLastId($id)
    {
        $this->last_id = $id;
    }

    public function setLastErrInfo(string $info)
    {
        $this->last_err_info = $info;
    }

    public function setLastErrCode($code)
    {
        $this->last_err_code = $code;
    }

    public function getLastParams(): array
    {
        return $this->last_params;
    }

    public function setLastParams($params)
    {
        $this->last_params = $params;
    }

    public function clear()
    {
        SwoftUtil::clean();
    }
}
